﻿using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Contacts;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Popups;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace test.XcoAppSpaces.Transport.Sockets
{
	[TestClass]
	public class SocketConnectionTests
	{
		[TestMethod]
		public void ConnectAndSendMessage()
		{
			Task.Run(async () =>
			{
				StreamSocketListener listener = new StreamSocketListener();
				listener.ConnectionReceived += listener_ConnectionReceived;
				//listener.BindEndpointAsync(new HostName("localhost"), "8000").AsTask().Wait(1000);
				await listener.BindServiceNameAsync("8000");
				Debug.WriteLine("Listener created, port=" + listener.Information.LocalPort);

				await Task.Delay(1000);

				var socket = new StreamSocket();
				await socket.ConnectAsync(new HostName("localhost"), "8000");
				Debug.WriteLine("Socket connected.");

				await Task.Delay(500);

				var data = new byte[] {1, 2, 3, 4};
				var writer = new DataWriter(socket.OutputStream);
				writer.WriteBytes(data);
				var ret = await writer.StoreAsync();
				//writer.DetachStream();
				//var stream = socket.OutputStream.AsStreamForWrite();
				//stream.Write(data, 0, data.Length);
				//var buffer = new Windows.Storage.Streams.Buffer(4);
				//buffer.AsStream().Write(data, 0, data.Length);
				//var result = await socket.OutputStream.WriteAsync(buffer);
				//await socket.OutputStream.FlushAsync();
				Debug.WriteLine("Data written " + ret);


				await Task.Delay(2000);

				socket.Dispose();
				Debug.WriteLine("Socket Disposed.");
			}).Wait();

		}

		void listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
		{
			Debug.WriteLine("Connection received!");

			var socket = args.Socket;
			Task.Run(async () =>
			{
				//var stream = args.Socket.InputStream.AsStreamForRead();
				//var buffer = new byte[4];
				//var numBytesRead = stream.Read(buffer, 0, buffer.Length);
				//var buffer = new Windows.Storage.Streams.Buffer(4);
				//var result = await socket.InputStream.ReadAsync(buffer, 4, InputStreamOptions.Partial);
				var reader = new DataReader(socket.InputStream);
				reader.InputStreamOptions = InputStreamOptions.Partial;
				var buffer = new byte[4];
				var result = await reader.LoadAsync((uint)buffer.Length);
				reader.ReadBytes(buffer);

				Debug.WriteLine("Read finished received! " + result + " bytes read. " + result);

				await Task.Delay(1000);

				args.Socket.Dispose();
			});
		}

		[TestMethod]
		public void Test2()
		{
			var test2 = new SocketsTest2();
			test2.Listen();

			Task.Delay(1000).Wait();

			test2.Connect();

			Task.Delay(1000).Wait();

			test2.Send();

			Task.Delay(1000).Wait();

			test2.Reply();

			Task.Delay(1000).Wait();

			test2.Dispose();
		}

	}

	public sealed partial class SocketsTest2
	{
		private StreamSocket _socket = new StreamSocket();
		private StreamSocketListener _listener = new StreamSocketListener();
		private List<StreamSocket> _connections = new List<StreamSocket>();
		private bool _connecting = false;
		private string connectionTarget = "localhost";


		public void Dispose()
		{
			if (_socket != null)
				_socket.Dispose();
			if (_listener != null)
				_listener.Dispose();
		}

		async private void WaitForData(StreamSocket socket)
		{
			try
			{
				var dr = new DataReader(socket.InputStream);
				//dr.InputStreamOptions = InputStreamOptions.Partial;
				var stringHeader = await dr.LoadAsync(4);

				if (stringHeader == 0)
				{
					LogMessage(string.Format("Disconnected (from {0})", socket.Information.RemoteHostName.DisplayName));
					return;
				}

				int strLength = dr.ReadInt32();

				uint numStrBytes = await dr.LoadAsync((uint)strLength);
				string msg = dr.ReadString(numStrBytes);

				LogMessage(string.Format("Received (from {0}): {1}", socket.Information.RemoteHostName.DisplayName, msg));

				WaitForData(socket);
			}
			catch (Exception e)
			{
				Debug.WriteLine(e);
			}
		}

		async public void Connect()
		{
			try
			{
				_connecting = true;
				await _socket.ConnectAsync(new HostName(connectionTarget), "3011");
				_connecting = false;
				
				LogMessage(string.Format("Connected to {0}", _socket.Information.RemoteHostName.DisplayName));

				WaitForData(_socket);
			}
			catch (Exception ex)
			{
				_connecting = false;
				Debug.WriteLine("Connect error: " + ex.Message);
			}
		}


		async public void Listen()
		{
			_listener.ConnectionReceived += listenerConnectionReceived;
			await _listener.BindServiceNameAsync("3011");

			LogMessage(string.Format("listening on {0}...", _listener.Information.LocalPort));
		}

		void listenerConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
		{
			_connections.Add(args.Socket);

			LogMessage(string.Format("Incoming connection from {0}", args.Socket.Information.RemoteHostName.DisplayName));

			WaitForData(args.Socket);
		}

		private void LogMessage(string message)
		{
			Debug.WriteLine(message);
		}

		public void Send()
		{
			SendMessage(_socket, "this is a test message");
		}

		async private void SendMessage(StreamSocket socket, string message)
		{
			var writer = new DataWriter(socket.OutputStream);
			var len = writer.MeasureString(message); // Gets the UTF-8 string length.
			writer.WriteInt32((int)len);
			writer.WriteString(message);
			var ret = await writer.StoreAsync();
			writer.DetachStream();

			LogMessage(string.Format("Sent (to {0}) {1}", socket.Information.RemoteHostName.DisplayName, message));
		}

		public void Reply()
		{
			foreach (var sock in _connections)
			{
				SendMessage(sock, "this is a test reply message");
			}
		}
	}
}
